﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Net;

namespace ktds_test_module
{
    public struct InfoTraceroute
    {
        public int Num_Hop;
        public String EndPoint;
        public int StartTime;
        public int StopTime;
        public int ProcessTime;
        public String Info_Message;
        public String Hostname;
    }

    class C_Traceroute
    {
        //=========================== 실제 - 트래이스 실행 함수 ===========================
        public List<InfoTraceroute> ExcuteTraceroute(String ipinfo)
        {
            List<InfoTraceroute> ResultTraceroute = new List<InfoTraceroute>();
            InfoTraceroute ST_ResultTraceroute = new InfoTraceroute();

            byte[] data = new byte[1024];
            int recv, timestart, timestop;
            Socket host = new Socket(AddressFamily.InterNetwork,
                  SocketType.Raw, ProtocolType.Icmp);
            IPHostEntry iphe = Dns.Resolve(ipinfo);
            IPEndPoint iep = new IPEndPoint(iphe.AddressList[0], 0);
            EndPoint ep = (EndPoint)iep;
            C_ICMP packet = new C_ICMP();
            packet.Type = 0x08;
            packet.Code = 0x00;
            packet.Checksum = 0;
            Buffer.BlockCopy(BitConverter.GetBytes(1), 0, packet.Message, 0, 2);
            Buffer.BlockCopy(BitConverter.GetBytes(1), 0, packet.Message, 2, 2);
            data = Encoding.ASCII.GetBytes("test packet");
            Buffer.BlockCopy(data, 0, packet.Message, 4, data.Length);
            packet.MessageSize = data.Length + 4;
            int packetsize = packet.MessageSize + 4;
            UInt16 chcksum = packet.getChecksum();
            packet.Checksum = chcksum;
            host.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 3000);
            int badcount = 0;

            for (int i = 1; i < 50; i++)
            {
                host.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, i);
                timestart = Environment.TickCount;
                host.SendTo(packet.getBytes(), packetsize, SocketFlags.None, iep);
                try
                {
                    data = new byte[1024];
                    recv = host.ReceiveFrom(data, ref ep);
                    timestop = Environment.TickCount;
                    C_ICMP response = new C_ICMP(data, recv);
                    if (response.Type == 11)
                    {
                        ST_ResultTraceroute.Num_Hop = i;
                        ST_ResultTraceroute.EndPoint = ep.ToString();
                        ST_ResultTraceroute.StartTime = timestart;
                        ST_ResultTraceroute.StopTime = timestop;
                        ST_ResultTraceroute.ProcessTime = timestop - timestart;
                        ST_ResultTraceroute.Info_Message = "Not Last Hop";

                        ResultTraceroute.Add(ST_ResultTraceroute);

                        // ResultTraceroute.Add("hop " + i + ": response from " + ep.ToString() + ", " + (timestop - timestart) + " ms");
                        // Console.WriteLine("hop {0}: response from {1}, {2}ms", i, ep.ToString(), timestop - timestart);
                    }

                    if (response.Type == 0)
                    {
                        ST_ResultTraceroute.Num_Hop = i;
                        ST_ResultTraceroute.EndPoint = ep.ToString();
                        ST_ResultTraceroute.StartTime = timestart;
                        ST_ResultTraceroute.StopTime = timestop;
                        ST_ResultTraceroute.ProcessTime = timestop - timestart;
                        ST_ResultTraceroute.Info_Message = "Last Hop";

                        ResultTraceroute.Add(ST_ResultTraceroute);
                        // ResultTraceroute.Add(ep.ToString() + " reached in " + i + " hops, " + (timestop - timestart) + " ms.");
                        // Console.WriteLine("{0} reached in {1} hops, {2}ms.", ep.ToString(), i, timestop - timestart);
                        break;
                    }
                    badcount = 0;
                }
                catch (SocketException)
                {
                    ST_ResultTraceroute.Num_Hop = i;
                    ST_ResultTraceroute.EndPoint = "*";
                    ST_ResultTraceroute.StartTime = 0;
                    ST_ResultTraceroute.StopTime = 0;
                    ST_ResultTraceroute.ProcessTime = 0;
                    ST_ResultTraceroute.Info_Message = "*";

                    ResultTraceroute.Add(ST_ResultTraceroute);
                    // ResultTraceroute.Add("hop " + i + ": No response from remote host");
                    // Console.WriteLine("hop {0}: No response from remote host", i);
                    badcount++;
                    if (badcount == 5)
                    {
                        ST_ResultTraceroute.Num_Hop = i;
                        ST_ResultTraceroute.EndPoint = "*";
                        ST_ResultTraceroute.StartTime = 0;
                        ST_ResultTraceroute.StopTime = 0;
                        ST_ResultTraceroute.ProcessTime = 0;
                        ST_ResultTraceroute.Info_Message = "*";

                        ResultTraceroute.Add(ST_ResultTraceroute);
                        // ResultTraceroute.Add("Unable to contact remote host");
                        // Console.WriteLine("Unable to contact remote host");
                        break;
                    }
                }
            }

            host.Close();
            return ResultTraceroute;
        }

        //=========================== 실제 - 2개의 트래이스 결과를 병합하는 함수 =========================== 
        public void MargeTwoTracerouteResult(List<string> lst_Src_Trace_Info, List<string> lst_Dst_Trace_Info)
        {
            
        }

        //=========================== 연습 - 트래이스 결과를 입력해서 파싱하는 함수 ===========================
        public String[] ParsingTracerouteResult(String ParaString)
        {
            String[] FirstSplitText = ParaString.Split(new string[] { "\r\n" }, StringSplitOptions.None);
            //String[] SecondSplitText = new String[100];
            String[] IpInfo = new String[100];

            for (int i = 0; i < FirstSplitText.Length; i++)
            {
                String[] SecondSplitText = FirstSplitText[i].Split(' ');

                for (int y = 0; y < SecondSplitText.Length; y++)
                {
                    IpInfo[i] = SecondSplitText[y];

                    if (IpInfo[i].Contains('[') || IpInfo[i].Contains(']'))
                    {
                        IpInfo[i] = IpInfo[i].Replace('[', ' ').Trim();
                        IpInfo[i] = IpInfo[i].Replace(']', ' ').Trim();
                    }
                }
            }
            return IpInfo;
        }
    }
}
